#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <nice2d/application.h>
#include <nice2d/tostring.h>
#include <nice2d/widgets/desktop.h>
#include <nice2d/widgets/progressbar.h>
#include <nice2d/components/fontmanager.h>

IMPLEMENT_OBJECT(ProgressBarStyle, ControlStyle)

ProgressBarStyle::ProgressBarStyle()
{
    hoverBackground = normalBackground;
    pressedBackground = normalBackground;
    disableBackground = normalBackground;
}

PTree ProgressBarStyle::serialize()
{
    PTree node;
    node.add_child("progressBarColor", serializeObject(progressBarColor));
    node.add("style", toString(style));
    node.add_child("textColor", serializeObject(textColor));
    return node;
}

void ProgressBarStyle::deserialize(const PTree &node)
{
    fontFile = node.get<std::string>("fontFile");
    characterSize = node.get<float>("characterSize");
    style = fromString(node.get<std::string>("style"), sf::Text::Style::Regular);
    textColor = deserializeColor(node.get_child("textColor"));
    progressBarColor = deserializeColor(node.get_child("progressBarColor"));
}

void ProgressBarStyle::sync()
{
}

class ProgressBarData
{
public:
    Orientation orientation = Orientation_H;
    sf::RectangleShape progressBarShape;
    bool textVisible = true;
    sf::Color textColor = sf::Color::White;
    std::shared_ptr<sf::Text> text;

    ProgressBar *progressBar = nullptr;

    ProgressBarData(ProgressBar *thisBar):
        progressBar(thisBar)
    {
        auto desktop = GET_APPLICATION_COMPONENT(Desktop);
        auto style = std::dynamic_pointer_cast<ProgressBarStyle>(desktop->getControlStyle("ProgressBar"));
        progressBar->setControlStyle(style);
    }

    void applyStyle()
    {
        auto style = progressBar->getTemplateStyle<ProgressBarStyle>();
        progressBarShape.setFillColor(style->progressBarColor);

        auto fontManager = GET_APPLICATION_COMPONENT(FontManager);
        auto font = fontManager->loadFromFile(style->fontFile);
        text->setFont(*font);
        text->setCharacterSize(style->characterSize);
        text->setStyle(style->style);
        text->setFillColor(style->textColor);

        progressBar->setBackground(style->normalBackground);
    }
};

IMPLEMENT_OBJECT(ProgressBar, AbstractSlider)

ProgressBar::ProgressBar():
    data(new ProgressBarData(this))
{
    data->text = std::make_shared<sf::Text>();
    data->applyStyle();
}

ProgressBar::~ProgressBar()
{
}

void ProgressBar::setTextVisible(bool visible)
{
    data->textVisible = visible;
}

bool ProgressBar::isTextVisible() const
{
    return data->textVisible;
}

void ProgressBar::setOrientation(Orientation o)
{
    if (data->orientation != o) {
        data->orientation = o;
        onValueChanged();
    }
}

Orientation ProgressBar::getOrientation()const
{
    return data->orientation;
}

void ProgressBar::onDrawControl(sf::RenderTarget &target, sf::RenderStates states) const
{
    target.draw(data->progressBarShape, states);
    if (data->textVisible)
        target.draw(*data->text, states);
}

void ProgressBar::onPositionChanged()
{
    onValueChanged();
}

void ProgressBar::onValueChanged()
{
    if (data->orientation == Orientation_H) {
        auto width = getWidth() * getValuePercent();
        data->progressBarShape.setSize(sf::Vector2f(width, getHeight()));
    } else {
        auto height = getHeight() * getValuePercent();
        data->progressBarShape.setSize(sf::Vector2f(getWidth(), height));
    }

    data->text->setString(std::to_string(getValue()) + "%");

    auto size = getSize();
    auto box = data->text->getLocalBounds();
    size.x = box.width;
    size.y = box.height;

    size = Entity::adjustPosition(sf::FloatRect(sf::Vector2f(), getSize()), size, HMode_Center,
                                  VMode_Center);
    size.y -= box.top;
    data->text->setPosition(size);
}

void ProgressBar::onStyleChanged()
{
    data->applyStyle();
}

